/******************************************************/
/* Goblin's Cube - Rubik's cube game for Wii Homebrew */
/* Copyright (C) 2009 Ricardo Bueno Cordeiro          */
/* Licensed under de GNU General Public License v2.0  */
/*   See license_gplv2.txt for detalis                */
/******************************************************/

#ifndef __CUBE_H_
#define __CUBE_H_

#include <tr1/random>
#include <gccore.h>

#include "sound.h"

#define MAX_NUM_INSTRUCTION (8 * 1024)
#define NUM_ANIMATION_STEPS 8

#define CB_Z_ROT_FOR_TOP_EDGE(edge) (((f32)((edge) % 4)) * 90.0f)

#define INSTRUCTION_TURN_LEFT  0
#define INSTRUCTION_TURN_RIGHT 1
#define INSTRUCTION_TURN_UP    2
#define INSTRUCTION_TURN_DOWN  3
#define INSTRUCTION_TURN_CW    4
#define INSTRUCTION_TURN_CC    5
#define INSTRUCTION_ROTATE_CW  6
#define INSTRUCTION_ROTATE_CC  7
#define INSTRUCTION_UNDO       8

#define CUBE_DRAW_ALL_PIECES   0
#define CUBE_DRAW_THIS_PIECE   1
#define CUBE_DRAW_OTHER_PIECES 2

#define PIECE_FRONT  0
#define PIECE_TOP    1
#define PIECE_RIGHT  2
#define PIECE_BOTTOM 3
#define PIECE_LEFT   4
#define PIECE_BACK   5

#define STATE_SOLVING 0
#define STATE_SOLVED  1

typedef double Color[3];

struct Edge;

struct Face {
	int central_color;
	int color[8];
	int edge[4];
	int owning_piece[8][4];
};

struct Edge {
	int face[2];
};

struct Instruction {
	int type;
	int piece_ref;
	int initiated;

	// Execution data
	int step;
	Vector *axis;
	f32 angle;
	int piece;
};

class Cube {
private:
	typedef void (Cube::*cube_member_func)(int);

	struct instruction_members_t {
		cube_member_func init;
		cube_member_func apply;
		cube_member_func undo;
	};

	static struct instruction_members_t instruction_members[];

	Sound cube_sound[4];
	
	int front_face;
	int top_edge;

	std::tr1::mt19937 generator;
	
	int last_scramble_movement;

	int num_rotations;
	long long initial_time;
	long long end_time;
	int state;

	Face face[6];
	Edge edge[12];

	Instruction instruction[MAX_NUM_INSTRUCTION];
	int num_inst;
	int current_inst;

	int	get_adjacent_face(int cur_face, int edge) const;
	int get_edge_index(int face, int edge) const;

	void add_owning_piece(int face, int color_pos, int piece);
	int owned_by(int face, int color_pos, int piece) const;

	// Draw
	int must_draw_face_piece(int face, int color_pos, int type, int piece) const;
	void draw_face_piece(double x_offset, double y_offset, int color) const;
	void draw_face(int face, f32 rotX, f32 rotY, f32 rotZ, int type, int piece) const;
	void draw_cube(int type, int piece) const;

	// Instructions
	void remove_instructions(int pos, int size);
	void insert_instruction(int type, int piece_ref);

	void apply_rotate_piece_cw_piece(int piece);
	void apply_rotate_piece_cc_piece(int piece);

	void apply_turn_left(int);
	void apply_turn_right(int);
	void apply_turn_up(int);
	void apply_turn_down(int);
	void apply_turn_cw(int);
	void apply_turn_cc(int);
	void apply_rotate_piece_cw(int pos);
	void apply_rotate_piece_cc(int pos);
	void apply_undo(int pos);

	void init_turn(int pos);
	void init_undo(int pos);
	void init_rotate_piece_cw(int pos);
	void init_rotate_piece_cc(int pos);

	// Scramble
	void scramble_piece();

	// Private constructor to enforce singleton
	Cube();

public:
	static Cube cube;

	void draw();
	
	void turn(int piece_from, int piece_to);
	void turn_left();
	void turn_right();
	void turn_up();
	void turn_down();
	void turn_cw();
	void turn_cc();
	void rotate_piece_cw(int piece_ref);
	void rotate_piece_cc(int piece_ref);
	void undo();

	void reset();
	void scramble();
	int is_solved();
	
	int get_num_rotations();
	f32 get_current_time();
};

#endif
